/*
 *===========================================================================
 *
 *          Name: jk_print.c
 *        Create: Sun 28 Oct 2012 11:53:44 AM CST
 *
 *   Discription: 
 *       Version: 1.0.0
 *
 *        Author: yuwei.zhang
 *         Email: yuwei.zhang@besovideo.com
 *
 *===========================================================================
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <sys/time.h>

#include "jkprint.h"


#define JK_COLOR_NONE	"\033[0m "
#define JK_COLOR_BLACK	"\033[0;30m "
#define JK_COLOR_DARK_GRAY		"\033[1;30m "
#define JK_COLOR_BLUE			"\033[0;34m "
#define JK_COLOR_LIGHT_BLUE	"\033[1;34m "
#define JK_COLOR_GREEN			"\033[0;32m "
#define JK_COLOR_LIGHT_GREE    "\033[1;32m "
#define JK_COLOR_CYAN			"\033[0;36m "
#define JK_COLOR_LIGHT_CYAN	"\033[1;36m "
#define JK_COLOR_RED			"\033[0;31m "
#define JK_COLOR_LIGHT_RED		"\033[1;31m "
#define JK_COLOR_PURPLE		"\033[0;35m "
#define JK_COLOR_LIGHT_PURPLE	"\033[1;35m "
#define JK_COLOR_BROWN			"\033[0;33m "
#define JK_COLOR_YELLOW		"\033[1;33m "
#define JK_COLOR_LIGHT_GRAY	"\033[0;37m "
#define JK_COLOR_WHITE			"\033[1;37m "


#define JK_NAME_LEN_SUPPORT              64
#define JK_MAX_STRING_LEN                512
#define JK_STRING_MAX_LEN_SUPPORT        3056

#define JKNAME(name)              \
    printf("[%s]", name)

#define JKTIME()                                                             \
  do{                                                                        \
    struct timeval _tNow; struct tm _tmNow;                                  \
    gettimeofday(&_tNow, NULL);                                              \
    memcpy(&_tmNow, localtime(&_tNow.tv_sec), sizeof(_tmNow));               \
    printf("[%04d-%02d-%02d][%02d:%02d:%02d.%03ld]",                         \
        _tmNow.tm_year+1900, _tmNow.tm_mon+1, _tmNow.tm_mday,                \
        _tmNow.tm_hour, _tmNow.tm_min, _tmNow.tm_sec, _tNow.tv_usec/1000);   \
  }while(0)

#define JKFUNC(func)             \
  printf("%s()", func)        

#define JKLINE(line)             \
  printf("%04d", line)        

#define JKFILE(file)             \
  printf("%s", file) 

#define JKLEFT()   printf("[")
#define JKRIGHT()  printf("]")
#define JKCOLON()  printf(":")
#define JKAT()     printf("@")

#define JKALL(name, func, line, file)             \
  JKNAME(name);                 \
  JKTIME();                     \
  JKLEFT();                     \
  JKFUNC(func);                 \
  JKCOLON();                    \
  JKLINE(line);                 \
  JKAT();                       \
  JKFILE(file);                 \
  JKRIGHT()

#define JKERRORTIPS(err)     printf("%s", err)
#define JKWARNTIPS(warn)     printf("%s", warn)
#define JKINFOTIPS(info)     printf("%s", info)
#define JKMESSAGETIPS(message)  printf("%s", message)
#define JKDEBUGTIPS(debug)   printf("%s", debug)
#define JKCYCLETIPS(cycle)   printf("%s", cycle)
#define JKTIPS(tips)         printf("%s", tips)

#define  JK_MAX_NAME_LEN            32

#define  JK_MAX_KIND_COUNTS         64

typedef struct __JK_Print {
    int              jk_print_level;
    int              jk_print_style;
    char             jk_name[JK_MAX_NAME_LEN+1];
    char             jk_style[JK_MAX_STRING_LEN+1];
    char            *jk_color;
    int              jk_enable_color;
    int              jk_used;
    int              jk_kind_cur;
    int              jk_kind[JK_MAX_KIND_COUNTS];
    FILE            *file;
} JK_Print;

static JK_Print jk_print_out = {
    JK_PRINT_ALL,
    JK_PRINT_STYLE_ALL,
    "jk_print",
    "",
    (char*)JK_COLOR_NONE,
    JK_COLOR_TRUE,
    0,0
};

static JK_Print *jk_p = &jk_print_out;

JK_EXTERN_C_FUNC int jk_print_init(int jk_print_level, 
              int jk_print_style, int jk_print_enable_color, 
              const char *name)
{
    if (jk_p != NULL) {
        if (jk_p->jk_used == 1) {
            printf("Warn: you have inited a print, exit...\n");
            return -3;
        }
    }

    jk_p->jk_enable_color = jk_print_enable_color;
    jk_p->jk_color = (char*)JK_COLOR_NONE;
    jk_p->jk_print_level = jk_print_level;
    jk_p->jk_print_style = jk_print_style;
    if (name != NULL) {
        int len = strlen(name);
        if (len > JK_NAME_LEN_SUPPORT || len > JK_MAX_NAME_LEN) {
            printf("WARN: long name [%d] > [%d] max support[%d], will be trancated\n", 
                   len, JK_MAX_NAME_LEN, JK_NAME_LEN_SUPPORT);
        }
        if (len <= 0) sprintf(jk_p->jk_name, "%s", "jk_print");
    } else {
        sprintf(jk_p->jk_name, "%s", "(jk_p)rint");
    }
    strncpy(jk_p->jk_name, name, sizeof(jk_p->jk_name));
    memset(jk_p->jk_style, 0, sizeof(jk_p->jk_style));

    jk_p->jk_used = 1;
    int i;
    for (i = 0; i < JK_MAX_KIND_COUNTS; i++) {
        jk_p->jk_kind[i] = 0;
    }

    return 0;
}

JK_EXTERN_C_FUNC int jk_print_deinit()
{
    if (jk_p) { jk_p->jk_used = 0; }
    return 0;
}

JK_EXTERN_C_FUNC int jk_print_set_color(int level)
{
    if ((jk_p)->jk_enable_color != JK_COLOR_TRUE) return -1;

    if (level & JK_PRINT_NONE) {
        (jk_p)->jk_color = (char*)JK_COLOR_NONE;
    } else if (level & JK_PRINT_ERROR) {
        (jk_p)->jk_color = (char*)JK_COLOR_RED;
    } else if (level & JK_PRINT_WARN) {
        (jk_p)->jk_color = (char*)JK_COLOR_YELLOW;
    } else if (level & JK_PRINT_INFO) {
        (jk_p)->jk_color = (char*)JK_COLOR_WHITE;
    } else if (level & JK_PRINT_DEBUG) {
        (jk_p)->jk_color = (char*)JK_COLOR_GREEN;
    } else if (level & JK_PRINT_CYCLE) {
        (jk_p)->jk_color = (char*)JK_COLOR_LIGHT_CYAN;
    } else if (level & JK_PRINT_MESSAGE) {
        (jk_p)->jk_color = (char*)JK_COLOR_LIGHT_GRAY;
    } else {
    	(jk_p)->jk_color = (char*)JK_COLOR_CYAN;
    }

    return 0;
}


JK_EXTERN_C_FUNC int jk_is_print_level_none(int level)
{
    if (jk_p == NULL) return -1;
    if (level & JK_PRINT_NONE) return -2;
    return 0;
}

// about name time, func....
JK_EXTERN_C_FUNC int jk_print_before(int type, const char *func, int line, const char *file)
{
    if (jk_p == NULL) return -1;
    char    pout[1024];

    int style = (jk_p)->jk_print_style;
    int has_done = 0;

    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        if ((jk_p)->jk_color != NULL)
            printf("%s", (jk_p)->jk_color);
    }
    if (type == -1) {
        memset((jk_p)->jk_style, 0, sizeof((jk_p)->jk_style));
        goto out;
    }

    if (style != JK_PRINT_STYLE_NONE) {
        switch(type) {
            case JK_PRINT_ERROR:
                JKERRORTIPS("[ERROR]**");
            break;
            case JK_PRINT_WARN:
                JKWARNTIPS("[WARN]*");
            break;
            case JK_PRINT_INFO:
                JKINFOTIPS("[INFO]");
            break;
            case JK_PRINT_MESSAGE:
                JKMESSAGETIPS("[MSG]");
            break;
            case JK_PRINT_DEBUG:
                JKDEBUGTIPS("[DEBUG]");
            break;
            case JK_PRINT_CYCLE:
                JKCYCLETIPS("[CYCLE]");
            break;
            default:
            break;
        }
    }
    if (style & JK_PRINT_STYLE_ALL) {
        JKALL((jk_p)->jk_name, func, line, file);
    } else if (style & JK_PRINT_STYLE_NONE) {
        memset((jk_p)->jk_style, 0, sizeof((jk_p)->jk_style));
        goto out;
    } else {

        if (strlen((jk_p)->jk_name) > 0 && !(style & JK_PRINT_NONAME)) {
            JKNAME((jk_p)->jk_name);
        } 
        if (!(style & JK_PRINT_NOTIME)) {
            JKTIME();
        } 
        if (!(style & JK_PRINT_NOFUNC)) {
            JKLEFT();JKFUNC(func);
            has_done = 1;
        } 
        if (!(style & JK_PRINT_NOLINE)) {
            if (has_done == 0) JKLEFT();
            else if (has_done == 1) JKCOLON();
            JKLINE(line);
            has_done = 1;
        }
        if (!(style & JK_PRINT_NOFILE)) { 
            if (has_done == 0) JKLEFT();
            else if (has_done == 1) JKAT();
            JKFILE(file);
            has_done = 1;
        }
        if (has_done == 1) JKRIGHT();
    }
    JKTIPS(" ==> ");

out:
    sprintf(pout, "%s", (jk_p)->jk_style);
    printf("%s", pout);

    return 0;
}

JK_EXTERN_C_FUNC int jk_print(int type, const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if ((jk_p)->jk_print_level & JK_PRINT_NONE) return -2;
    if (!((jk_p)->jk_print_level & type)) return -3;

    jk_print_set_color(type);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(type, func, line, file);
    printf("%s", out);

    va_end(arg_ptr);

    return 0;
}

JK_EXTERN_C_FUNC int jk_print_style_none(const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    //if (!(jk_p->jk_print_level & JK_PRINT_NONE)) return -2;
    jk_print_set_color(JK_PRINT_DEBUG);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));
    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(-1, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }
    va_end(arg_ptr);

    return 0;
}

JK_EXTERN_C_FUNC int jk_print_error(const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0)  return -2;
    jk_print_set_color(JK_PRINT_ERROR);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_ERROR, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
}

JK_EXTERN_C_FUNC int jk_print_stderror(const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0)  return -2;
    jk_print_set_color(JK_PRINT_ERROR);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_ERROR, func, line, file);
    printf("%s", out);
    perror("- ");
    printf("\n");
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
}

JK_EXTERN_C_FUNC int jk_print_warn(const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0) return -2;
    if (!((jk_p)->jk_print_level & JK_PRINT_WARN)) return -2;
    jk_print_set_color(JK_PRINT_WARN);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_WARN, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
} 

JK_EXTERN_C_FUNC int jk_print_info(const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0) return -2;
    if (!((jk_p)->jk_print_level & JK_PRINT_INFO)) return -2;
    jk_print_set_color(JK_PRINT_INFO);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_INFO, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
} 

JK_EXTERN_C_FUNC int jk_print_message(const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0) return -2;
    //if (!(jk_p->jk_print_level & JK_PRINT_INFO)) return -2;
    jk_print_set_color(JK_PRINT_MESSAGE);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_MESSAGE, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
} 

JK_EXTERN_C_FUNC int jk_print_debug(const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0) return -2;
    if (!((jk_p)->jk_print_level & JK_PRINT_DEBUG)) return -2;
    jk_print_set_color(JK_PRINT_DEBUG);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_DEBUG, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
} 

JK_EXTERN_C_FUNC int jk_print_cycle(const char *func, int line, const char *file, const char *format, ...)
{
    if (jk_p == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0) return -2;
    if (!((jk_p)->jk_print_level & JK_PRINT_CYCLE)) return -2;
    jk_print_set_color(JK_PRINT_CYCLE);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_CYCLE, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
} 

JK_EXTERN_C_FUNC int jk_print_change_level(int level)
{
    if ((jk_p) == NULL) return -1;

    return (jk_p)->jk_print_level = level;
}

JK_EXTERN_C_FUNC int jk_print_start_cycle()
{
    if ((jk_p) == NULL) return -1;
 
    return (jk_p)->jk_print_level |= JK_PRINT_CYCLE;
}

JK_EXTERN_C_FUNC int jk_print_stop_cycle()
{
    if ((jk_p) == NULL) return -1;

    return (jk_p)->jk_print_level &= ~JK_PRINT_CYCLE;
}
 
JK_EXTERN_C_FUNC int jk_print_start_debug()
{
    if ((jk_p) == NULL) return -1;

    return (jk_p)->jk_print_level |= JK_PRINT_DEBUG;
}

JK_EXTERN_C_FUNC int jk_print_stop_debug()
{
    if ((jk_p) == NULL) return -1;

    return (jk_p)->jk_print_level &= ~JK_PRINT_DEBUG;
}


JK_EXTERN_C_FUNC int jk_print_add_kind(int kind)
{
    if (!jk_p) return -1;
    int i = 0;
    for (i = 0; i < JK_MAX_KIND_COUNTS; i++) {
        if (jk_p->jk_kind[i] == 0) {
            jk_p->jk_kind[i] = kind;
            jk_p->jk_kind_cur++;
            break;
        }
    }
    return jk_p->jk_kind_cur;
}

JK_EXTERN_C_FUNC int jk_print_del_kind(int kind)
{
    if (!jk_p) return -1;
    int i;
    for (i = 0; i < JK_MAX_KIND_COUNTS; i++) {
        if (jk_p->jk_kind[i] == 0) break;
        if (jk_p->jk_kind[i] == kind) {
            jk_p->jk_kind[i] = jk_p->jk_kind[jk_p->jk_kind_cur-1];
            jk_p->jk_kind_cur--;
        }
    }
	return jk_p->jk_kind_cur;
}

JK_EXTERN_C_FUNC int jk_print_kind(int kind, const char *func, int line, const char *file, const char *format, ...)
{
    if ((jk_p) == NULL) return -1;
    if (jk_is_print_level_none((jk_p)->jk_print_level) < 0) return -2;
    int i;
    for (i = 0; i < jk_p->jk_kind_cur; i++) {
        if (jk_p->jk_kind[i] == 0) break;
        if (jk_p->jk_kind[i] == kind) break;
    }
    if (i == jk_p->jk_kind_cur) return -3;
    jk_print_set_color(0);

    char  out[JK_STRING_MAX_LEN_SUPPORT];
    memset(out, 0, sizeof(out));

    va_list arg_ptr;
    va_start(arg_ptr, format);
    vsprintf(out, format, arg_ptr);

    jk_print_before(JK_PRINT_DEBUG, func, line, file);
    printf("%s", out);
    if ((jk_p)->jk_enable_color == JK_COLOR_TRUE) {
        printf(JK_COLOR_NONE);
    }

    va_end(arg_ptr);

    return 0;
}

JK_EXTERN_C_FUNC int jk_open_file(const char *path)
{
    if (jk_p == NULL) return -1;

    jk_p->file = fopen(path, "w+");
    if (!jk_p->file) return -2;

    return 0;
}

JK_EXTERN_C_FUNC int jk_close_file()
{
    if (jk_p == NULL) return -1;
  
    if (jk_p->file) { fclose(jk_p->file); jk_p->file = NULL; }

    return 0;
}


/*=============== End of file: jk_print.c ==========================*/

